#!/usr/bin/env python

import wx, sys, re, os
from EvacuateRoutingAlgorithm import GetEvacuateList, Widget, DiagramMotion, RectCenter, MakeRect, find_if



# Invalid_Point = wx.Point(-1,-1)
ID_AddWidgetManually = 1
ID_AddWidgetAutomatically = 2
ID_MoveWidget = 3

g_MenuItems = [
    [ID_AddWidgetManually, "Add Widget Manually"],
    [ID_AddWidgetAutomatically, "Add Widget Automatically"],    
    ]

        
class MainFrame(wx.Frame):
    def __init__(self, parent, title):
        self.boundary = wx.Rect(0,0, 220,800)
        wx.Frame.__init__(self, parent, name=title, size=self.boundary.Size, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
        #self.control = []
        #self.control.append( wx.TextCtrl(self, style = wx.TE_MULTILINE) )
        #self.CreateStatusBar()
        
        #menu = wx.Menu()
        #menuExit = menu.Append(wx.ID_EXIT, 'Exit', 'Terminate Program')
        #menuBar = wx.MenuBar()
        #menuBar.Append(menu, 'File')
        
        
        Width, Height = self.GetClientSizeTuple()
        mbarH = self.boundary.Height - Height
        #Width, Height = self.GetSizeTuple()
        self.SetSize(wx.Size(self.boundary.width, self.boundary.Height + mbarH))
                
        self._Buffer = wx.EmptyBitmap(*self.boundary.Size)
        self.memoryDc = wx.MemoryDC()
        self.memoryDc.SelectObject(self._Buffer)
        self.WidgetList = [
            Widget(1, wx.Rect(0,5,100,100)),
            Widget(2, wx.Rect(100,5, 100,100)),
            Widget(3, wx.Rect( 5,250, 100,100)),
            Widget(4, wx.Rect(65,150, 100,100)),
            Widget(5, wx.Rect( 0,400, 100,100)),
            ]
        self.isDirty = True
        self.status = 0
        self.point0 = None
        self.prevCursor = None
        self.pos_before_moved = None
        self.movingWidget = None
        
        wx.EVT_PAINT(self, self.OnPaint)
        #self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
        wx.EVT_PAINT(self, self.OnPaint)
        wx.EVT_LEFT_DOWN(self, self.OnLButtonDown)
        wx.EVT_LEFT_UP(self, self.OnLButtonUp)
        wx.EVT_RIGHT_UP(self, self.OnRButtonUp)
        wx.EVT_MOTION(self, self.OnMouseMove)
        #self.SetMenuBar(menuBar)
    
                    
    def OnExit(self, event):
        self.Close(True)
    def NewWidgetId(self):
        for i in range(1, 256):
            if self.FindWidgetById(i) is None:
                return i
        return None
    
    def FindWidgetById(self, id):
        for wg in self.WidgetList:
            if wg.id == id:
                return wg
        return None
    
            
    def OnMouseMove(self, event):
        point = event.Position
        if self.status == ID_MoveWidget:
            # print '[OnMouseMove] ID_MoveWidget'
            offset = point - self.prevCursor;
            newRect = MakeRect( self.pos_before_moved + offset, self.movingWidget.rect.Size )
            self.movingWidget.rect = newRect
            self.SetDirty()
        elif self.status == ID_AddWidgetManually:
            if self.point0 is not None:
                self.SetDirty()
            self.prevCursor = point

                
    def OnLButtonDown(self, event):
        point = event.Position
        if self.status == 0:
            movingWidget = self.GetWidgetFromClick(point)
            # print "[OnLButtonDown] movingWidget = %s" % movingWidget
            if movingWidget is not None:
                self.movingWidget = movingWidget
                self.WidgetList.remove(movingWidget)
                self.pos_before_moved = self.movingWidget.rect.Position
                self.status = ID_MoveWidget
                # '[OnLButtonDown] self.status=%d' % self.status
                self.CaptureMouse()
                self.prevCursor = point

    def GetWidgetFromClick(self, pos):
        for wg in self.WidgetList:
            if wg.rect.Contains(pos):
                return wg
        return None
    
    def GetCollidedWidget(self, rect):
        for wg in self.WidgetList:
            if wg.rect.Intersects(rect):
                return wg
        return None
    
    def OnLButtonUp(self, event):
        point = event.Position
        if self.status == ID_MoveWidget:
            # print '[OnLButtonUp] OnLButtonUp'
            self.SetDirty()
            if self.GetCollidedWidget(self.movingWidget.rect) is not None and self.IsInBound(self.movingWidget.rect):
                for wg in self.WidgetList:
                    print '%s, collided=%s' % (str(wg), wg.rect.Intersects(self.movingWidget.rect) )
                evaList = GetEvacuateList(self.movingWidget, self.WidgetList, self.boundary)
                if evaList is []:
                    self.movingWidget.rect = MakeRect(self.pos_before_moved, self.movingWidget.rect.Size)
                else:
                    for diagramMotion in evaList:
                        id,motion = diagramMotion.id, diagramMotion.motion
                        widget = find_if(self.WidgetList, lambda wg:wg.id == id)
                        widget.rect = motion.ApplyOnRect( widget.rect  )
                        
            elif not self.IsInBound(self.movingWidget.rect):
                self.movingWidget.rect = MakeRect(self.pos_before_moved, self.movingWidget.rect.Size)
            #if self.GetCollidedWidget(self.movingWidget.rect) is not None or not self.IsInBound(self.movingWidget.rect):

            self.WidgetList.append(self.movingWidget)
            self.movingWidget = None
            self.pos_before_moved = None
            self.ReleaseMouse()
            self.status = 0
            
        elif self.status == ID_AddWidgetManually:
            if self.point0 is None:
                if self.GetWidgetFromClick(event.Position) is None:
                    self.point0 = event.Position
                    self.CaptureMouse()
                    self.prevCursor = event.Position
            else:
                newRect = MakeRect(self.point0, event.Position)
                if self.GetCollidedWidget(newRect) is None:
                    id = self.NewWidgetId()
                    self.WidgetList.append( Widget(id, newRect))
                    self.ReleaseMouse()
                    self.status = 0
                    self.point0 = None
                    self.SetDirty()
                    
    def SetDirty(self):
        self.isDirty = True
        self.Refresh(eraseBackground=False)
    
    def OnRButtonUp(self, event):
        menu = wx.Menu()
        for (id,title) in g_MenuItems:
            ### 3. Launcher packs menu with Append. ###
            menu.Append( id, title )
            ### 4. Launcher registers menu handlers with EVT_MENU, on the menu. ###
            wx.EVT_MENU( menu, id, self.OnPopupMenuItem )

        ### 5. Launcher displays menu with call to PopupMenu, invoked on the source component, passing event's GetPoint. ###
        self.PopupMenu( menu, event.Position )
        
    def OnPopupMenuItem(self, event):
        menuId = event.GetId()
        if menuId in [1,2]:
            self.status = menuId

        
    def OnPaint(self, event):
        dc = wx.PaintDC(self)
        # print '[OnPaint] isDirty=%s' % self.isDirty
        if self.isDirty :
            self.Render()
            isDirty = False        
        dc.Blit(0,0, self._Buffer.Width, self._Buffer.Height, self.memoryDc, 0,0)

    def Render(self):
        dc = self.memoryDc
        dc.SetBackground( wx.WHITE_BRUSH )
        dc.Clear() # make sure you clear the bitmap!

        dc.SetBackgroundMode(wx.TRANSPARENT)
        dc.SetTextForeground(wx.Color(0,0,0))
        dc.SetBrush(wx.Brush(wx.Color(255,200,200)))
        dc.SetPen(wx.BLACK_PEN)
        for dg in self.WidgetList:
            dc.DrawRectangle(*dg.rect)
            dc.DrawText(str(dg.id), *RectCenter(dg.rect))

        if self.status == ID_MoveWidget:
            self.DrawMovingWidget(self.movingWidget.rect)
            dc.DrawText(str(self.movingWidget.id), *RectCenter(self.movingWidget.rect))
        elif self.status == ID_AddWidgetManually:
            if self.point0 is not None:
                newRect = MakeRect(self.point0, self.prevCursor)
                self.DrawMovingWidget(newRect)
        
    def DrawMovingWidget(self, rect):
        self.DrawHollowRect(rect)
        if self.GetCollidedWidget( rect ) is not None or not self.IsInBound(rect):            
            self.DrawDiagonalCross(rect);
            
    def IsInBound(self, rect):
        return self.boundary.ContainsRect(rect)
        
    def DrawHollowRect(self, rect):
        dc = self.memoryDc
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.BLACK_PEN)
        dc.DrawRectangle(*rect)
        
    def DrawDiagonalCross(self, rect):
        dc = self.memoryDc
        dc.SetPen( wx.Pen(wx.Color(255,0,0), 3) )
        dc.DrawLine(rect.left+1, rect.top+1,   rect.Right, rect.Bottom );
        dc.DrawLine(rect.Right, rect.top+1,  rect.left+1, rect.Bottom );
    
def main():
    app = wx.App(redirect=False)
    frame = MainFrame(None, 'Widget Layout Manager')
    frame.Show(True)
    app.MainLoop()
        
    
if __name__ == "__main__":
    main()

